/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package OpenCodes.Persistencia;

import OpenCodes.Persistencia.MySQL.Ciudad.ICiudadRepositorio;
import OpenCodes.Persistencia.MySQL.IRepositorioGenerico;
import OpenCodes.Persistencia.MySQL.Persona.IPersonaRepositorio;
import OpenCodes.Persistencia.MySQL.Jugador.IJugadorRepositorio;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ServiceLoader;

/**
 *
 * @author Administrador
 */
public abstract class AbstractDB {

    private final ThreadLocal<Connection> THREAD_CONNECTION = new ThreadLocal<Connection>();
    private final Properties propiedades;
    // Tipo de motor
    private static String MOTOR;
    // propiedades de conexión
    private final String servidor;
    private final String base;
    private final String usuario;
    private final String contrasenia;

    /* Singleton */
    private static AbstractDB singleton = null;

    /**
     * Al instanciar cualquier fabrica, es tomarán las propiedades de conexion
     * del archivo jdbc.properties
     *
     * @throws RuntimeException
     *             en caso de no poder cargar el archivo jdbc.properties
     */
    public AbstractDB() {
        try {
            propiedades = new Properties();
            propiedades.load(getClass().getResourceAsStream("/jdbc.properties"));
            // parametros de conexion
            MOTOR = propiedades.getProperty("jdbc.motor");
            servidor = propiedades.getProperty("jdbc.servidor");
            base = propiedades.getProperty("jdbc.base");
            usuario = propiedades.getProperty("jdbc.usuario");
            contrasenia = propiedades.getProperty("jdbc.contrasenia");
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Falló lectura de archivo de configuracion", ex);
        }
    }

    /**
     * Crea una instancia de tipo {@link AbstractDB} en función de configuración
     * de servicio realizada en META-INF.sevices
     *
     * @return Impletación de {@link AbstractDB}
     */
    public static AbstractDB getInstance() {

        if (singleton == null) {


            ServiceLoader<AbstractDB> fabricasDisponibles = ServiceLoader.load(AbstractDB.class);
            //String fabricasDisponibles;


            for (AbstractDB fabrica : fabricasDisponibles) {
                if (fabrica.getMotor().equals(MOTOR)) {
                    singleton = fabrica;
                }
            }
            if (singleton == null) {
                throw new RuntimeException(
                        "No se encontró implementación para el motor " + MOTOR);
            }
        }

        return singleton;

    }

    /**
     * Establece la conexión con la base de datos especificada.
     *
     * @return conexión con la base de datos.
     * @throws RepositorioException
     */
    public Connection getConnection() throws RepositorioException {

        //lee las variables de la clase MysqlDb que contiene los datos de la conexion mysql

        String url = getUrl(servidor, base);
        String driver = getDriver();
        // Carga el driver en memoria
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            throw new RepositorioException(
                    "No se encontró el driver " + driver, e);
        }

        // Conecta con la base de datos
        Connection conn = THREAD_CONNECTION.get();
        try {
            if (conn == null || conn.isClosed()) {
                conn = DriverManager.getConnection(url, usuario, contrasenia);
                THREAD_CONNECTION.set(conn);
            }
        } catch (SQLException e) {
            throw new RepositorioException("Fallo la conexión", e);
        }

        return conn;
    }

    //---------- Repositorios -----------------//

        /* Persona
        public abstract IPersonaRepositorio getPersonaRepositorio()
        throws RepositorioException;
         */
    /*
    //Generico
     public abstract IRepositorioGenerico getRepositorioGenerico()
            throws RepositorioException;
     * */
    //Jugador
    public abstract IJugadorRepositorio getJugadorRepositorio()
            throws RepositorioException;
    //Ciudad
    public abstract ICiudadRepositorio getCiudadRepositorio()
            throws RepositorioException;

    /*-------------------------------------*/
    /* Metodos Template */
    protected abstract String getUrl(String servidor, String base);

    protected abstract String getDriver();

    protected abstract String getMotor();
}
